# pip install -r requirements.txt

# TODO download rom with proxy
# TODO press right requires desktop is active http://stackoverflow.com/questions/66080433

import subprocess
import requests
import json
import multithread  # dep -> tqdm
import os
import hashlib
import tarfile
import shutil
import pywinauto
import pyautogui
import time
import xmltodict
import pathlib
import datetime
import errno
import func_timeout

url = 'http://ci.openharmony.cn/api/daily_build/build/list/component'
payload = {
    "pageNum": 1,
    "pageSize": 8,
    "startTime": "",
    "endTime": "",
    "projectName": "openharmony",
    "branch": "master",
    "component": "dayu200_merge",
    "deviceLevel": "rich",
    "hardwareBoard": "",
    "buildStatus": "success",
    "buildFailReason": "",
    "type": 1,
    "sortType": "",
    "sortField": ""
}
use_proxy = False
proxies = {
    'http': 'http://proxynj.huawei.com:8080',
    'https': 'http://proxynj.huawei.com:8080',
}
auth = ('工号', '密码')
rom_download_aiohttp_args = {
    "method": "GET",
    "timeout": 2 * 60 * 60,  # 2 hours, default timeout is 600 seconds = 5 minutes, which is not enough for downloading.
}
max_wait_flash_time = 120  # seconds
max_wait_boot_time = 20  # seconds
sleep_time = 3  # seconds


def get_roms_list():
    if use_proxy:
        roms = requests.post(url, json=payload, proxies=proxies, auth=auth)
    else:
        roms = requests.post(url, json=payload)
    return json.loads(roms.content.decode(encoding='utf-8'))['data']['list']['dataList']


def get_rom_sha256(rom_sha256_url):
    print(f'begin download {rom_sha256_url}')
    rom_sha256_name = os.path.basename(rom_sha256_url)
    print(f'download into {rom_sha256_name}')
    download_object = multithread.Downloader(rom_sha256_url, rom_sha256_name)
    download_object.start()
    print('download done')
    with open(rom_sha256_name, 'r+') as rom_sha256_file:
        rom_sha256 = rom_sha256_file.readline().split(' ')[0]
    print(rom_sha256)
    return rom_sha256


def calc_file_sha256(filename):
    try:
        print(f'calc sha256 for file {filename}')
        with open(filename, "rb") as f:
            bytes_content = f.read()  # read entire file as bytes
            readable_hash = hashlib.sha256(bytes_content).hexdigest()
            print(readable_hash)
            return readable_hash
    except FileNotFoundError as _:
        print(f'calc sha256 failed, return empty')
        return ''


def download_rom(rom_download_url, rom_file_name):
    print(f'begin download {rom_download_url}')
    download_object = multithread.Downloader(rom_download_url, rom_file_name, aiohttp_args=rom_download_aiohttp_args)
    download_object.start()
    print('download done')


def system(cmd, encoding='utf-8', timeout=None):
    print('>', cmd)
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if timeout is not None:
        try:
            process.wait(timeout=timeout)
            stdout, stderr = func_timeout.func_timeout(timeout=timeout, func=process.communicate)
        except (subprocess.TimeoutExpired, func_timeout.exceptions.FunctionTimedOut):
            print(f'cmd {cmd} timeout!')
            return '', '', -1
    else:
        process.wait()
        stdout, stderr = process.communicate()
    stdout = stdout.decode(encoding=encoding)
    stderr = stderr.decode(encoding=encoding)
    return_code = process.returncode
    print(f'stdout = {stdout}')
    print(f'stderr = {stderr}')
    print(f'return code = {return_code}')
    return stdout, stderr, return_code


def get_result_from_xml_file(xml_result_file):
    xml_tree = xmltodict.parse(pathlib.Path(xml_result_file).read_text())['testsuites']

    total_count = int(xml_tree['@tests'])
    fail_count = int(xml_tree['@failures'])
    disable_count = int(xml_tree['@disabled'])
    error_count = int(xml_tree['@errors'])

    print(f'xml_result_file = {xml_result_file}')
    print(f'total_count = {total_count}')
    print(f'fail_count = {fail_count}')
    print(f'disable_count = {disable_count}')
    print(f'error_count = {error_count}')
    print('')

    if total_count == 0:
        print('error: total count is 0!')
        return -1
    if fail_count != 0 or disable_count != 0 or error_count != 0:
        print('error: not all case success!')
        return -1
    return 0


def remove_if_exists(file):
    print(f'removing {file}')
    try:
        os.remove(file)
    except OSError as e:
        if e.errno != errno.ENOENT:
            raise


def reboot_device(app_main_window):
    print('reboot device now')
    tab_window = app_main_window.child_window(class_name='SysTabControl32')  # 下载镜像
    tab_window.type_keys('{RIGHT}')  # 升级固件
    tab_window.type_keys('{RIGHT}')  # 高级功能
    reboot_button = app_main_window.child_window(title='重启设备')
    reboot_button.click()
    print('press reboot button done, reboot action may fail, but ignore result')


def get_device_current_rom_version():
    print('get device current rom version')
    system('hdc shell cat /proc/version', timeout=5)


def analyze_cmds_and_results(result_lines, cmd_keyword, result_keyword, success_keyword):
    cmds = [l for l in result_lines if cmd_keyword in l]
    results = [l for l in result_lines if result_keyword in l]
    all_success_results = [l for l in results if success_keyword in l]

    fail = 0

    if len(all_success_results) != len(results):
        print('有一些xts用例失败了！请在日志里边搜索以下内容！')
        print(*list(set(results) - set(all_success_results)), sep='\n')
        fail -= 1

    if len(cmds) != len(results):
        print('有一些xts用例没有输出汇总结果！xts运行失败！看一下下边哪个命令没有结果吧！')
        print(*[l for l in result_lines if cmd_keyword in l or result_keyword in l], sep='\n')
        fail -= 1

    return fail


def analyze_xts_results(filename):
    xts_results = pathlib.Path(filename).read_text()
    result_lines = xts_results.splitlines()

    fail = analyze_cmds_and_results(
        result_lines, 'hdc shell aa', 'OHOS_REPORT_RESULT', 'Failure: 0, Error: 0,')
    fail += analyze_cmds_and_results(
        result_lines, 'gtest_output=xml', 'testsuites tests', 'failures="0" disabled="0" errors="0"')

    return fail


def main():
    get_device_current_rom_version()
    print('check whether hdc works')
    system('hdc list targets', timeout=5)

    print('remove result xml files')
    remove_if_exists('huks_module_test.xml')
    remove_if_exists('huks_UT_test.xml')
    remove_if_exists('huks_multithread_test.xml')
    remove_if_exists('hukssdk_test.xml')
    remove_if_exists('huks_useridm_wrap_test.xml')
    remove_if_exists('crypto_engine_unit_test.xml')
    remove_if_exists('huks_mt_test.xml')

    roms_list = get_roms_list()
    selected_rom = roms_list[0]
    print(f'selected rom is {selected_rom}')
    try:
        rom_test_result_success = selected_rom['testData'][0]['testSuccess'] == 'true'  # 冒烟测试
    except TypeError:
        rom_test_result_success = True  # '失败原因' 列为空时(即selected_rom['testData']为None)认为整包测试成功

    rom_download_url = selected_rom['imgObsPath']
    rom_file_name = os.path.basename(rom_download_url)
    print(f'selected rom is {rom_download_url}')
    print(f'selected rom is {rom_file_name}')

    print('begin compile and download latest tdd and xts')
    return os.system('pwsh -ExecutionPolicy Bypass -File get_xts.ps1') # 只拉代码编译用例，不下载刷机跑了

    rom_sha256_url = selected_rom['obsSha256Path']
    rom_sha256_expect = get_rom_sha256(rom_sha256_url)
    rom_sha256_actual = calc_file_sha256(rom_file_name)
    print(f'expect rom sha256 is {rom_sha256_expect}')
    print(f'actual rom sha256 is {rom_sha256_actual}')

    if rom_sha256_expect == rom_sha256_actual:
        print('already downloaded, do not need download again')
    else:
        print(f'download new file {rom_download_url}')
        try:
            os.remove(rom_file_name)
        except FileNotFoundError as _:
            pass
        download_rom(rom_download_url, rom_file_name)

    print(f'begin decompress rom file {rom_file_name}')
    rom_dir = 'version'
    print(f'rom decompressed dir {rom_dir}')
    if os.path.exists(rom_dir):
        print(f'remove existing dir {rom_dir}')
        shutil.rmtree(rom_dir, ignore_errors=True)
    with tarfile.open(rom_file_name) as rom_tar_object:
        print(f'begin exrtract {rom_file_name} into {rom_dir}')
        rom_tar_object.extractall(rom_dir)
    print(f'decompress done {rom_dir}')

    print('rebooting into bootloader')
    std_output, _, _ = system('hdc shell reboot loader', timeout=5)  # raise an error if hdc stuck
    if '[Fail]ExecuteCommand need connect-key' in std_output:
        raise Exception('设备状态异常，没有hdc设备了，请检查设备状态')

    print('begin downloading flash tool if not exist')
    system('git clone https://gitee.com/hihope_iot/docs.git hihope_iot-docs --depth=1')
    system(r'copy /Y config.cfg hihope_iot-docs\HiHope_DAYU200\烧写工具及指南\windows', encoding='gb2312')
    print('begin flash')
    rk_dev_tool_exe = r'hihope_iot-docs\HiHope_DAYU200\烧写工具及指南\windows\RKDevTool.exe'
    rk_dev_tool_exe = os.getcwd() + '\\' + rk_dev_tool_exe
    rk_dev_tool_path = os.path.dirname(rk_dev_tool_exe)
    print(f'path = {rk_dev_tool_path}')
    app = pywinauto.application.Application().start(rk_dev_tool_exe, work_dir=rk_dev_tool_path)
    print('start flash tool ok')
    app_main_window = app.window(title='瑞芯微开发工具 v2.84')
    begin_process_button = app_main_window.window(title='执行')
    print('begin flash')
    begin_process_button.click()
    flashing = True
    wait_time = 0
    flash_result_img = 'flash_result.png'
    while flashing:
        print('still flashing, please wait')
        time.sleep(sleep_time)
        if wait_time > max_wait_flash_time:
            pyautogui.screenshot(flash_result_img)
            print('error: flash took too long')
            if rom_test_result_success:
                print('error: rom ok, please check your environment!')
                return -1
            else:
                print('rom 冒烟测试 失败，本次测试认为成功')
                reboot_device(app_main_window)
                return 0
        wait_time += sleep_time
        try:
            app_main_window.window(title='没有发现设备').click()
            print('flash done')
            flashing = False
        except pywinauto.findwindows.ElementNotFoundError:
            pass
    print('flash ok')

    pyautogui.screenshot(flash_result_img)

    booting = True
    wait_time = 0
    while booting:
        print('device is booting, please wait')
        time.sleep(sleep_time)
        if wait_time > max_wait_boot_time:
            print('error: booting took too long')
            return -1
        wait_time += sleep_time
        try:
            app_main_window.window(title='发现一个MASKROM设备').click()
            print('device boot done')
            booting = False
        except pywinauto.findwindows.ElementNotFoundError:
            pass
    print('device already boot')
    print('kill flash tool')
    app.kill()
    print('wait 10 seconds for startup animation')
    time.sleep(10)

    print('Set flow control by process to disabled')
    system('hdc shell hilog -Q pidoff', timeout=5)
    print('Log persistance task control')
    system('hdc shell hilog -w start', timeout=5)

    print('set device date time')
    time_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    print(f'set device date time to {time_str}')
    system(f'hdc shell date \'{time_str}\'', timeout=5)
    print('get device date time')
    system(f'hdc shell date', timeout=5)

    get_device_current_rom_version()

    print('begin run tdd')
    os.system('cmd /c run.bat')

    tdd_result = 0
    tdd_result += get_result_from_xml_file('huks_module_test.xml')
    tdd_result += get_result_from_xml_file('huks_UT_test.xml')
    tdd_result += get_result_from_xml_file('huks_multithread_test.xml')
    tdd_result += get_result_from_xml_file('hukssdk_test.xml')
    tdd_result += get_result_from_xml_file('huks_useridm_wrap_test.xml')
    tdd_result += get_result_from_xml_file('crypto_engine_unit_test.xml')
    tdd_result += get_result_from_xml_file('huks_mt_test.xml')
    tdd_result += get_result_from_xml_file('huks_storage_test.xml')
    tdd_result += get_result_from_xml_file('huks_file_transfer_config_parser_test.xml')
    tdd_result += get_result_from_xml_file('huks_mock_test.xml')
    tdd_result += get_result_from_xml_file('service_ipc_test.xml')

    if tdd_result != 0:
        print('error: some tdd case failed!')
    else:
        print('tdd all pass!!!')

    system(f'hdc shell ls -al /data/service/el1/public/huks_service/maindata/', timeout=5)
    system(f'hdc shell chown -R huks_server:huks_server /data/service/el1/public/huks_service/maindata/0', timeout=5)
    system(f'hdc shell ls -al /data/service/el1/public/huks_service/maindata/', timeout=5)

    print('开机后没有任何操作进入桌面的时候，第一次调用hdc aa拉起应用必失败，所以先拉一下别的东西失败一下，然后再拉起测试用例就不会失败了')
    os.system('hdc shell aa start -a MainAbility -b ohos.samples.etsclock')

    print('begin run xts')
    xts_result_file_name = 'run_xts.result.txt'
    remove_if_exists(xts_result_file_name)
    os.system(r'venv\Scripts\python.exe -u .\run_xts.py | C:\cygwin64\bin\tee.exe ' + xts_result_file_name)
    print('end run xts')
    xts_result = analyze_xts_results(xts_result_file_name)
    if xts_result != 0:
        print('error: some xts case failed!')
    else:
        print('xts all pass!!!')

    return tdd_result + xts_result


if __name__ == '__main__':
    exit(main())
